#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include "COMMON.H"

typedef struct CardOrCell
{
	byte Suit;
	byte Value;
	byte CellType;
	short LastX;
	short LastY;
	struct CardOrCell *Parent;
	struct CardOrCell *Child;
} CardOrCell;

#define NUM_CARDS 40
#define NUM_FREE_CELLS 3
#define NUM_FOUNDATION_CELLS 3
#define NUM_TABLEAU_CELLS 8

#define SUIT_CELL 0
#define SUIT_COINS 1
#define SUIT_BAMBOO 2
#define SUIT_CHARS 3
#define SUIT_DRAGON_RED 4
#define SUIT_DRAGON_GREEN 5
#define SUIT_DRAGON_WHITE 6
#define SUIT_FLOWERS 7

#define CELL_FREE 0
#define CELL_FOUNDATION 1
#define CELL_TABLEAU 2
#define CELL_FLOWER 3

#define MODE_IDLE 0
#define MODE_DRAGGING 1
#define MODE_AUTOMOVING 2
#define MODE_WON 3

#define CARD_WIDTH 30
#define CARD_HEIGHT 61

static Screen This;

Rect RedDragonButtonBounds = { 123, 9, 15, 14 };
bool RedDragonButtonHovered;
Rect GreenDragonButtonBounds = { 123, 31, 15, 14 };
bool GreenDragonButtonHovered;
Rect WhiteDragonButtonBounds = { 123, 53, 15, 14 };
bool WhiteDragonButtonHovered;
Rect NewGameButtonBounds = { 239, 223, 71, 14 };
bool NewGameButtonHovered;
static Rect InstructionsButtonBounds = { 169, 223, 71, 14 };
static bool InstructionsButtonHovered;

byte Mode = MODE_WON;

CardOrCell Cards[NUM_CARDS];
CardOrCell FreeCells[NUM_FREE_CELLS];
CardOrCell FoundationCells[NUM_FOUNDATION_CELLS];
CardOrCell TableauCells[NUM_TABLEAU_CELLS];
CardOrCell FlowerCell;

bool DealingCards;
byte NextDealTarget;
CardOrCell *DraggedCard;
CardOrCell *DragAbortParent;
short DragOffsetX;
short DragOffsetY;
byte FreeCellsDragonSuit[NUM_FREE_CELLS];
CardOrCell *AutoMoveCard;
short AutoMoveStartX;
short AutoMoveStartY;
short AutoMoveProgress;
bool WinFanfarePending;

//==============================================================================
// Card logic functions
//==============================================================================

void InitializeEmptyCell(CardOrCell *cell, byte cellType)
{
	cell->Suit = SUIT_CELL;
	cell->Value = 0;
	cell->CellType = cellType;
	cell->Parent = NULL;
	cell->Child = NULL;
}

void MoveCard(CardOrCell *card, CardOrCell *newParent)
{
	// Detach from the previous parent, if there is one:
	if (card->Parent != NULL)
	{
		card->Parent->Child = NULL;
		card->Parent = NULL;
	}

	// Attach to the new parent:
	card->Parent = newParent;
	newParent->Child = card;
}

CardOrCell *GetTopOfStack(CardOrCell *base)
{
	while (base->Child != NULL)
	{
		base = base->Child;
	}

	return base;
}

CardOrCell *GetBaseOfStack(CardOrCell *top)
{
	while (top->Parent != NULL)
	{
		top = top->Parent;
	}

	return top;
}

bool IsCardValueCard(CardOrCell *card)
{
	return card->Suit == SUIT_COINS || card->Suit == SUIT_BAMBOO || card->Suit == SUIT_CHARS;
}

bool CanCardBePickedUp(CardOrCell *card)
{
	short i;
	CardOrCell *cardBase = GetBaseOfStack(card);

	// Only cards can actually be picked up:
	if (card->Suit == SUIT_CELL)
	{
		return false;
	}

	// Don't allow cards to be removed from consumed free cells:
	if (cardBase->Suit == SUIT_CELL && cardBase->CellType == CELL_FREE)
	{
		for (i = 0; i < NUM_FREE_CELLS; i++)
		{
			if (FreeCellsDragonSuit[i] != SUIT_CELL && cardBase == &FreeCells[i])
			{
				return false;
			}
		}
	}

	// Cards placed in a foundation pile or flower cell can never be picked up:
	if (cardBase->Suit == SUIT_CELL && (cardBase->CellType == CELL_FOUNDATION || cardBase->CellType == CELL_FLOWER))
	{
		return false;
	}

	// Cards can be picked up as long the cards on top of them alternate correctly:
	while (card->Child != NULL)
	{
		if (!IsCardValueCard(card->Child) || card->Child->Suit == card->Suit || card->Child->Value != card->Value - 1)
		{
			return false;
		}

		card = card->Child;
	}

	return true;
}

bool CanCardBeDropped(CardOrCell *card, CardOrCell *target)
{
	// Cards can never be dropped onto a card that isn't the topmost in its stack:
	if (target->Child != NULL)
	{
		return false;
	}

	// The rules for dropping a card vary depending on what type of stack it is:
	switch (GetBaseOfStack(target)->CellType)
	{
		case CELL_TABLEAU:
			// Cards can be moved into empty tableau slots or on top of cards of a different suit with a greater value:
			return (target->Suit == SUIT_CELL && target->CellType == CELL_TABLEAU) || (IsCardValueCard(card) && IsCardValueCard(target) && card->Suit != target->Suit && card->Value == target->Value - 1);

		case CELL_FOUNDATION:
			// Only the next card in a sequence can be moved into a foundation pile:
			return card->Child == NULL && IsCardValueCard(card) && IsCardValueCard(target) && card->Suit == target->Suit && card->Value == target->Value + 1;

		case CELL_FREE:
			// Only a single card can be moved into a dragon cell:
			return card->Child == NULL && target->Suit == SUIT_CELL && target->CellType == CELL_FREE;

		case CELL_FLOWER:
			// Cards can never be manually dragged into a flower cell:
			return false;
	}

	// This shouldn't cover any cases, but just in case we should deny the move:
	return false;
}

/* If the specified suit of dragons can be stacked, returns a non-null target where they can be moved. */
CardOrCell *GetDragonStackTarget(byte suit)
{
	short i;
	short dragonCount = 0;
	CardOrCell *target = NULL;

	for (i = NUM_FREE_CELLS - 1; i >= 0; i--)
	{
		CardOrCell *card = GetTopOfStack(&FreeCells[i]);
		if (card->Suit == suit)
		{
			dragonCount += 1;
			target = card;
		}
		else if (card->Suit == SUIT_CELL)
		{
			target = card;
		}
	}

	for (i = 0; i < NUM_TABLEAU_CELLS; i++)
	{
		CardOrCell *card = GetTopOfStack(&TableauCells[i]);
		if (card->Suit == suit)
		{
			dragonCount += 1;
		}
	}

	return (dragonCount == 4)
		? target 
		: NULL;
}

void StartNewGame(void)
{
	short i, j;
	short shuffle[NUM_CARDS];

	// Initialize the deck of cards:
	for (i = 0; i < NUM_CARDS; i++)
	{
		Cards[i].Value = 0;
		Cards[i].LastX = 0;
		Cards[i].LastY = 0;
		Cards[i].Parent = NULL;
		Cards[i].Child = NULL;

		if (i <= 8) 
		{
			Cards[i].Suit = SUIT_COINS;
			Cards[i].Value = i + 1;
		}
		else if (i >= 9 && i <= 17)
		{
			Cards[i].Suit = SUIT_BAMBOO;
			Cards[i].Value = i - 8;
		} 
		else if (i >= 18 && i <= 26) 
		{
			Cards[i].Suit = SUIT_CHARS;
			Cards[i].Value = i - 17;
		}
		else if (i >= 27 && i <= 30) 
		{
			Cards[i].Suit = SUIT_DRAGON_RED;
		}
		else if (i >= 31 && i <= 34) 
		{
			Cards[i].Suit = SUIT_DRAGON_GREEN;
		}
		else if (i >= 35 && i <= 38) 
		{
			Cards[i].Suit = SUIT_DRAGON_WHITE;
		}
		else
		{
			Cards[i].Suit = SUIT_FLOWERS;
		}
	}

	// Shuffle the deck using Fischer-Yates:
	for (i = 0; i < NUM_CARDS; i++)
	{
		shuffle[i] = i;
	}

	for (i = 0; i <= NUM_CARDS - 2; i++)
	{
		byte temp = shuffle[i];
		j = rand() % (NUM_CARDS - i) + i;
		shuffle[i] = shuffle[j];
		shuffle[j] = temp;
	}

	// Stack the cards in the flower cell:
	InitializeEmptyCell(&FlowerCell, CELL_FLOWER);
	MoveCard(&Cards[shuffle[0]], &FlowerCell);
	for (i = 0; i < NUM_CARDS - 1; i++)
	{
		MoveCard(&Cards[shuffle[i + 1]], &Cards[shuffle[i]]);
	}

	// Initialize the rest of the cells:
	for (i = 0; i < NUM_FREE_CELLS; i++)
	{
		InitializeEmptyCell(&FreeCells[i], CELL_FREE);
		FreeCellsDragonSuit[i] = SUIT_CELL;
	}

	for (i = 0; i < NUM_FOUNDATION_CELLS; i++)
	{
		InitializeEmptyCell(&FoundationCells[i], CELL_FOUNDATION);
	}

	for (i = 0; i < NUM_TABLEAU_CELLS; i++)
	{
		InitializeEmptyCell(&TableauCells[i], CELL_TABLEAU);
	}

	// Start the process of dealing cards:
	Mode = MODE_IDLE;
	DealingCards = true;
	NextDealTarget = 0;
}

bool IsGameWon(void)
{
	short i;

	for (i = 0; i < NUM_TABLEAU_CELLS; i++)
	{
		if (TableauCells[i].Child != NULL)
		{
			return false;
		}
	}

	return true;
}

bool IsCardAutoMoving(CardOrCell *card)
{
	return Mode == MODE_AUTOMOVING && card == AutoMoveCard;
}

//==============================================================================
// Drawing functions
//==============================================================================

void DrawCardFront(byte suit, byte value, short x, short y)
{
	Blit(IMG_CARD_FRONT_TOP, x + 1, y);
	Blit(IMG_CARD_FRONT, x, y + 1);
	Blit(IMG_CARD_BOTTOM, x + 1, y + 60);

	switch (suit)
	{
		case SUIT_COINS:
			Blit(FONT_NUMBERS_RED[value - 1], x + 3, y + 3);
			Blit(FONT_SYMBOLS[2], x + 3, y + 10);
			Blit(FONT_SYMBOLS_FLIPPED[2], x + 22, y + 44);
			Blit(FONT_NUMBERS_FLIPPED_RED[value - 1], x + 22, y + 52);
			break;
		case SUIT_BAMBOO:
			Blit(FONT_NUMBERS_GREEN[value - 1], x + 3, y + 3);
			Blit(FONT_SYMBOLS[1], x + 3, y + 10);
			Blit(FONT_SYMBOLS_FLIPPED[1], x + 22, y + 44);
			Blit(FONT_NUMBERS_FLIPPED_GREEN[value - 1], x + 22, y + 52);
			break;
		case SUIT_CHARS:
			Blit(FONT_NUMBERS_BLACK[value - 1], x + 3, y + 3);
			Blit(FONT_SYMBOLS[0], x + 3, y + 10);
			Blit(FONT_SYMBOLS_FLIPPED[0], x + 22, y + 44);
			Blit(FONT_NUMBERS_FLIPPED_BLACK[value - 1], x + 22, y + 52);
			break;
		case SUIT_DRAGON_RED:
			Blit(FONT_SYMBOLS[5], x + 3, y + 3);
			Blit(FONT_SYMBOLS_FLIPPED[5], x + 22, y + 51);
			break;
		case SUIT_DRAGON_GREEN:
			Blit(FONT_SYMBOLS[4], x + 3, y + 3);
			Blit(FONT_SYMBOLS_FLIPPED[4], x + 22, y + 51);
			break;
		case SUIT_DRAGON_WHITE:
			Blit(FONT_SYMBOLS[3], x + 3, y + 3);
			Blit(FONT_SYMBOLS_FLIPPED[3], x + 22, y + 51);
			break;
		case SUIT_FLOWERS:
			Blit(FONT_SYMBOLS[6], x + 3, y + 3);
			Blit(FONT_SYMBOLS_FLIPPED[6], x + 22, y + 51);
			break;
	}
}

void DrawCardFrontStub(byte suit, byte value, short x, short y)
{
	Blit(IMG_CARD_FRONT_TOP, x + 1, y);
	Blit(IMG_CARD_FRONT_STUB, x, y + 1);

	switch (suit)
	{
		case SUIT_COINS:
			Blit(FONT_NUMBERS_RED[value - 1], x + 3, y + 3);
			break;
		case SUIT_BAMBOO:
			Blit(FONT_NUMBERS_GREEN[value - 1], x + 3, y + 3);
			break;
		case SUIT_CHARS:
			Blit(FONT_NUMBERS_BLACK[value - 1], x + 3, y + 3);
			break;
		case SUIT_DRAGON_RED:
			Blit(FONT_SYMBOLS[5], x + 3, y + 3);
			break;
		case SUIT_DRAGON_GREEN:
			Blit(FONT_SYMBOLS[4], x + 3, y + 3);
			break;
		case SUIT_DRAGON_WHITE:
			Blit(FONT_SYMBOLS[3], x + 3, y + 3);
			break;
		case SUIT_FLOWERS:
			Blit(FONT_SYMBOLS[6], x + 3, y + 3);
			break;
	}
}

void DrawCardStack(CardOrCell *base, bool faceUp, bool stackUp, byte stackUpDivider, short x, short y)
{
	// NOTE: As we render cards, we save out their positions in LastX/LastY so that we'll later
	// know when we've clicked on them. This is a little dodgy, but it makes it so that we know
	// where everything with only a single calculation.

	short stackUpCount = 0;

	while (base != NULL)
	{
		base->LastX = x;
		base->LastY = y;

		if (base->Suit != SUIT_CELL && !IsCardAutoMoving(base))
		{
			// Draw the card:
			if (base->Child == NULL || IsCardAutoMoving(base->Child))
			{
				if (faceUp)
				{
					DrawCardFront(base->Suit, base->Value, x, y);
				}
				else
				{
					Blit(IMG_CARD_BACK_TOP, x + 1, y);
					Blit(IMG_CARD_BACK, x, y + 1);
					Blit(IMG_CARD_BOTTOM, x + 1, y + 60);
				}
			}
			else
			{
				if (stackUp)
				{
					if (stackUpCount == 0)
					{
						if (faceUp)
						{
							Blit(IMG_CARD_FRONT_STUB_BOTTOM, x, y + 58);
							Blit(IMG_CARD_BOTTOM, x + 1, y + 60);
						}
						else
						{
							Blit(IMG_CARD_BACK_STUB_BOTTOM, x, y + 58);
							Blit(IMG_CARD_BOTTOM, x + 1, y + 60);
						}
					}
				}
				else
				{
					if (faceUp)
					{
						DrawCardFrontStub(base->Suit, base->Value, x, y);
					}
					else
					{
						Assert(false, "Drawing face-down, stack-down cards is not supported.");
					}
				}
			}

			// Advance to the next card's position:
			if (stackUp)
			{
				stackUpCount += 1;
				if (stackUpCount == stackUpDivider)
				{
					stackUpCount = 0;
					y -= 2;
				}
			}
			else
			{
				y += 10;
			}
		}

		base = base->Child;
	}
}

void DrawDragonButton(byte suit, byte normalImage, byte hoverImage, byte disabledImage, bool hovered, short x, short y)
{
	byte buttonImage = disabledImage;

	if (DraggedCard == NULL && GetDragonStackTarget(suit) != NULL)
	{
		buttonImage = hovered ? hoverImage : normalImage;
	}

	Blit(buttonImage, x, y);
}

void DrawRedDragonButton(void)
{
	DrawDragonButton(SUIT_DRAGON_RED, IMG_DRAGON_RED, IMG_DRAGON_RED_HOVER, IMG_DRAGON_RED_DISABLED, RedDragonButtonHovered, RedDragonButtonBounds.X, RedDragonButtonBounds.Y);
}

void DrawGreenDragonButton(void)
{
	DrawDragonButton(SUIT_DRAGON_GREEN, IMG_DRAGON_GREEN, IMG_DRAGON_GREEN_HOVER, IMG_DRAGON_GREEN_DISABLED, GreenDragonButtonHovered, GreenDragonButtonBounds.X, GreenDragonButtonBounds.Y);
}

void DrawWhiteDragonButton(void)
{
	DrawDragonButton(SUIT_DRAGON_WHITE, IMG_DRAGON_WHITE, IMG_DRAGON_WHITE_HOVER, IMG_DRAGON_WHITE_DISABLED, WhiteDragonButtonHovered, WhiteDragonButtonBounds.X, WhiteDragonButtonBounds.Y);
}

void DrawInstructionsButton(void)
{
	Blit(InstructionsButtonHovered ? IMG_BUTTON_INSTRUCTIONS_HOVER : IMG_BUTTON_INSTRUCTIONS, InstructionsButtonBounds.X, InstructionsButtonBounds.Y);
}

void DrawNewGameButton(void)
{
	Blit(NewGameButtonHovered ? IMG_BUTTON_NEW_GAME_HOVER : IMG_BUTTON_NEW_GAME, NewGameButtonBounds.X, NewGameButtonBounds.Y);
}

void DrawWinCount(void)
{
	char winCountText[16];

	DrawText("WINS", 14, 229);

	itoa(WinCount, winCountText, 10);
	DrawTextCenter(winCountText, 49, 229);
}

void DrawBottomBar(void)
{
	Blit(IMG_GAME_BOTTOM_BAR, 0, 223);
	DrawInstructionsButton();
	DrawNewGameButton();
	DrawWinCount();
}

#define ShouldDrawStack(stack) (drawEverything || firstStack == &stack || secondStack == &stack)

static void DrawScreen(CardOrCell *firstStack, CardOrCell *secondStack)
{
	short i;
	bool drawEverything = (firstStack == NULL && secondStack == NULL);
	bool drawBottomBar = drawEverything;

	if (drawEverything)
	{
		Blit(IMG_GAME_BACKGROUND_03, 120, 0);
	}

	if (ShouldDrawStack(FlowerCell))
	{
		Blit(IMG_GAME_BACKGROUND_04, 148, 0);
		DrawCardStack(&FlowerCell, !DealingCards, true, 6, 152, 9);
	}

	for (i = 0; i < NUM_FREE_CELLS; i++)
	{
		if (ShouldDrawStack(FreeCells[i]))
		{
			Blit(IMG_GAME_BACKGROUND_00 + i, 40 * i, 0);
			DrawCardStack(&FreeCells[i], FreeCellsDragonSuit[i] == SUIT_CELL, true, 3, 8 + 39 * i, 9);
		}
	}

	for (i = 0; i < NUM_FOUNDATION_CELLS; i++)
	{
		if (ShouldDrawStack(FoundationCells[i]))
		{
			Blit(IMG_GAME_BACKGROUND_05 + i, 199 + 39 * i, 0);
			DrawCardStack(&FoundationCells[i], true, true, 6, 203 + 39 * i, 9);
		}
	}

	for (i = 0; i < NUM_TABLEAU_CELLS; i++)
	{
		if (ShouldDrawStack(TableauCells[i]))
		{
			short x = (i >= 4) ? (40 * i - 1) : (40 * i);
			Blit(IMG_GAME_BACKGROUND_08 + i, x, 74);
			DrawCardStack(&TableauCells[i], true, false, 0, 8 + 39 * i, 79);
			drawBottomBar = true;
		}
	}

	if (drawBottomBar)
	{
		DrawBottomBar();
	}

	// We always need to draw the dragon buttons when we redraw part of the screen
	// because we don't catch the event when these buttons become clickable.
	DrawRedDragonButton();
	DrawGreenDragonButton();
	DrawWhiteDragonButton();
}

//==============================================================================
// State helper functions
//==============================================================================

void StartAutoMove(CardOrCell *card, CardOrCell *newParent)
{
	CardOrCell *oldBaseOfStack;

	Mode = MODE_AUTOMOVING;
	AutoMoveCard = card;
	AutoMoveStartX = card->LastX;
	AutoMoveStartY = card->LastY;
	AutoMoveProgress = 0;

	oldBaseOfStack = GetBaseOfStack(card);
	MoveCard(card, newParent);

	DrawScreen(oldBaseOfStack, GetBaseOfStack(newParent));
}

CardOrCell *GetCardOrCellUnderMouse(void)
{
	short i;

	for (i = 0; i < NUM_TABLEAU_CELLS + NUM_FREE_CELLS + NUM_FOUNDATION_CELLS; i++)
	{
		CardOrCell *cell;
		if (i < NUM_TABLEAU_CELLS) cell = &TableauCells[i];
		else if (i < NUM_TABLEAU_CELLS + NUM_FREE_CELLS) cell = &FreeCells[i - NUM_TABLEAU_CELLS];
		else cell = &FoundationCells[i - NUM_TABLEAU_CELLS - NUM_FREE_CELLS];

		cell = GetTopOfStack(cell);

		while (cell != NULL)
		{
			Rect cellBounds;

			cellBounds.X = cell->LastX;
			cellBounds.Y = cell->LastY;
			cellBounds.W = CARD_WIDTH;
			cellBounds.H = CARD_HEIGHT;

			if (RectContains(&cellBounds, MouseX, MouseY))
			{
				return cell;
			}

			cell = cell->Parent;
		}
	}

	return NULL;
}

void PlayWinFanfare(void)
{
	delay(110);
	sound(110);
	delay(220);
	sound(147);
	delay(440);
	nosound();
}

short GetAutoMoveSteps(void)
{
	return DealingCards ? 3 : 6;
}

//==============================================================================
// State functions
//==============================================================================

static void OnActivate()
{
	DrawScreen(NULL, NULL);
}

static void OnUpdate()
{
	short i, j, pass;

	// Wrap up auto-moves that have finished:
	if (Mode == MODE_AUTOMOVING)
	{
		AutoMoveProgress += 1;
		if (AutoMoveProgress > GetAutoMoveSteps())
		{
			// Detect the end of dealing here so that we don't animate the final card slower than the rest:
			if (DealingCards && FlowerCell.Child == NULL)
			{
				DealingCards = false;
			}

			Mode = MODE_IDLE;
			PlayClick();
			DrawScreen(GetBaseOfStack(AutoMoveCard), NULL);
		}
		else
		{
			delay(1);
		}
	}

	// Look for new auto-moves:
	if (Mode == MODE_IDLE)
	{
		if (DealingCards)
		{
			// Deal a card to the tableau:
			StartAutoMove(GetTopOfStack(&FlowerCell), GetTopOfStack(&TableauCells[NextDealTarget]));
			NextDealTarget = (NextDealTarget + 1) % NUM_TABLEAU_CELLS;
		}
		else
		{
			short minFoundationValue = 0;
			for (i = 0; i < NUM_FOUNDATION_CELLS; i++)
			{
				short foundationValue = GetTopOfStack(&FoundationCells[i])->Value;
				minFoundationValue = (i == 0) ? foundationValue : Min(foundationValue, minFoundationValue);
			}

			// Do this in two passes so that we always move dragons first:
			for (pass = 0; pass < 2; pass++)
			{
				for (i = 0; i < NUM_TABLEAU_CELLS + NUM_FREE_CELLS; i++)
				{
					CardOrCell *card = (i < NUM_TABLEAU_CELLS)
						? GetTopOfStack(&TableauCells[i])
						: GetTopOfStack(&FreeCells[i - NUM_TABLEAU_CELLS]);

					// Don't consider free cells that have been committed to storing a suit of dragon cards:
					if (i >= NUM_TABLEAU_CELLS && FreeCellsDragonSuit[i - NUM_TABLEAU_CELLS] != SUIT_CELL)
					{
						continue;
					}

					if (pass == 1 && card->Suit == SUIT_FLOWERS)
					{
						// Move the flowers card to the flowers cell:
						StartAutoMove(card, &FlowerCell);
					}

					if (pass == 0 && (card->Suit == SUIT_DRAGON_RED || card->Suit == SUIT_DRAGON_GREEN || card->Suit == SUIT_DRAGON_WHITE))
					{
						// Move dragon cards to free cells allocated to them:
						for (j = 0; j < NUM_FREE_CELLS; j++)
						{
							if (FreeCellsDragonSuit[j] == card->Suit)
							{
								StartAutoMove(card, GetTopOfStack(&FreeCells[j]));
							}
						}
					}

					if (pass == 1 && (card->Suit == SUIT_COINS || card->Suit == SUIT_BAMBOO || card->Suit == SUIT_CHARS))
					{
						// Move value cards to the foundation cells:
						for (j = 0; j < NUM_FOUNDATION_CELLS; j++)
						{
							if (card->Suit == SUIT_COINS + j)
							{
								CardOrCell *topOfFoundationStack = GetTopOfStack(&FoundationCells[j]);
								if (card->Value == topOfFoundationStack->Value + 1 && card->Value - 2 <= minFoundationValue)
								{
									StartAutoMove(card, topOfFoundationStack);
								}
							}
						}
					}

					// If a card was auto-moved we should wait until the next OnUpdate() to auto-move another:
					if (Mode != MODE_IDLE)
					{
						pass = 2;
						break;
					}
				}
			}
		}
	}

	// HACK: This needs to go BEFORE the code below to play on the next update:
	if (WinFanfarePending)
	{
		WinFanfarePending = false;
		PlayWinFanfare();

		// Save after playing the win fanfare so that we don't pause 
		// awkwardly before playing it when saving to disk:
		SaveWinCount();
	}

	// Detect if the game is won:
	if (Mode == MODE_IDLE && IsGameWon())
	{
		Mode = MODE_WON;
		WinFanfarePending = true;
		WinCount = Min(WinCount + 1, 9999);
		DrawBottomBar();
	}

	// Hover detection for UI buttons:
	if (Mode == MODE_IDLE || Mode == MODE_WON)
	{
		if (RedDragonButtonHovered != RectContains(&RedDragonButtonBounds, MouseX, MouseY))
		{
			RedDragonButtonHovered = !RedDragonButtonHovered;
			DrawRedDragonButton();
		}

		if (GreenDragonButtonHovered != RectContains(&GreenDragonButtonBounds, MouseX, MouseY))
		{
			GreenDragonButtonHovered = !GreenDragonButtonHovered;
			DrawGreenDragonButton();
		}

		if (WhiteDragonButtonHovered != RectContains(&WhiteDragonButtonBounds, MouseX, MouseY))
		{
			WhiteDragonButtonHovered = !WhiteDragonButtonHovered;
			DrawWhiteDragonButton();
		}

		if (InstructionsButtonHovered != RectContains(&InstructionsButtonBounds, MouseX, MouseY))
		{
			InstructionsButtonHovered = !InstructionsButtonHovered;
			DrawInstructionsButton();
		}

		if (NewGameButtonHovered != RectContains(&NewGameButtonBounds, MouseX, MouseY))
		{
			NewGameButtonHovered = !NewGameButtonHovered;
			DrawNewGameButton();
		}
	}
	else
	{
		RedDragonButtonHovered = false;
		GreenDragonButtonHovered = false;
		WhiteDragonButtonHovered = false;
		InstructionsButtonHovered = false;
		NewGameButtonHovered = false;
	}
}

static void OnKeyDown(short key)
{
	if (key == KEY_ESCAPE)
	{
		PlayClick();
		ActivateScreen(DesktopScreen);
	}
}

void OnMouseDown_DragonButton(bool hovered, byte suit)
{
	short i;

	if (hovered)
	{
		CardOrCell *target = GetDragonStackTarget(suit);
		if (target != NULL)
		{
			CardOrCell *base = GetBaseOfStack(target);
			for (i = 0; i < NUM_FREE_CELLS; i++)
			{
				if (base == &FreeCells[i])
				{
					FreeCellsDragonSuit[i] = suit;
				}
			}
			
			Mode = MODE_AUTOMOVING;
			PlayClick();

			switch (suit)
			{
				case SUIT_DRAGON_RED:
					DrawRedDragonButton();
					break;
				case SUIT_DRAGON_GREEN:
					DrawGreenDragonButton();
					break;
				case SUIT_DRAGON_WHITE:
					DrawWhiteDragonButton();
					break;
			}
		}
	}
}

static void OnMouseDown()
{
	if (Mode == MODE_IDLE)
	{
		CardOrCell *card = GetCardOrCellUnderMouse();
		if (card != NULL && card->Suit != SUIT_CELL && CanCardBePickedUp(card))
		{
			CardOrCell *oldBaseOfStack = GetBaseOfStack(card);

			Mode = MODE_DRAGGING;
			DraggedCard = card;
			DragAbortParent = card->Parent;
			DragOffsetX = card->LastX - MouseX;
			DragOffsetY = card->LastY - MouseY;
			card->Parent->Child = NULL;
			card->Parent = NULL;
			
			PlayClick();
			DrawScreen(oldBaseOfStack, NULL);
		}
	}

	if (NewGameButtonHovered)
	{
		PlayClick();
		StartNewGame();
		DrawScreen(NULL, NULL);
	}

	if (InstructionsButtonHovered)
	{
		PlayClick();
		ActivateScreen(InstructionsScreen);
	}

	OnMouseDown_DragonButton(RedDragonButtonHovered, SUIT_DRAGON_RED);
	OnMouseDown_DragonButton(GreenDragonButtonHovered, SUIT_DRAGON_GREEN);
	OnMouseDown_DragonButton(WhiteDragonButtonHovered, SUIT_DRAGON_WHITE);
}

static void OnMouseUp()
{
	if (Mode == MODE_DRAGGING)
	{
		short i;
		CardOrCell *dropTarget = DragAbortParent;
		int dropTargetDistance = 10000;
		Rect draggedCardBounds;

		draggedCardBounds.X = MouseX + DragOffsetX;
		draggedCardBounds.Y = MouseY + DragOffsetY;
		draggedCardBounds.W = CARD_WIDTH;
		draggedCardBounds.H = CARD_HEIGHT;

		for (i = 0; i < NUM_TABLEAU_CELLS + NUM_FREE_CELLS + NUM_FOUNDATION_CELLS; i++)
		{
			CardOrCell *cell;
			if (i < NUM_TABLEAU_CELLS) cell = &TableauCells[i];
			else if (i < NUM_TABLEAU_CELLS + NUM_FREE_CELLS) cell = &FreeCells[i - NUM_TABLEAU_CELLS];
			else cell = &FoundationCells[i - NUM_TABLEAU_CELLS - NUM_FREE_CELLS];

			cell = GetTopOfStack(cell);

			if (cell != NULL)
			{
				Rect cellBounds;

				cellBounds.X = cell->LastX;
				cellBounds.Y = cell->LastY;
				cellBounds.W = CARD_WIDTH;
				cellBounds.H = CARD_HEIGHT;

				if (RectOverlaps(&draggedCardBounds, &cellBounds) && CanCardBeDropped(DraggedCard, cell))
				{
					int cellDistance = (int)(sqrt(
						pow(draggedCardBounds.X - cellBounds.X, 2) + 
						pow(draggedCardBounds.Y - cellBounds.Y, 2)));

					if (cellDistance < dropTargetDistance)
					{
						dropTarget = cell;
						dropTargetDistance = cellDistance;
					}
				}
			}
		}

		MoveCard(DraggedCard, dropTarget);
		DraggedCard = NULL;
		Mode = MODE_IDLE;
		PlayClick();
		DrawScreen(GetBaseOfStack(dropTarget), NULL);
	}
}

static short DrawDragContent_Lerp(short a, short b)
{
	short autoMoveSteps = GetAutoMoveSteps();
	short autoMoveProgress = Min(AutoMoveProgress, autoMoveSteps);
	return (short)(a + (b - a) * autoMoveProgress / autoMoveSteps);
}

static void DrawDragContent()
{
	if (DraggedCard != NULL)
	{
		DrawCardStack(DraggedCard, true, false, 0, MouseX + DragOffsetX, MouseY + DragOffsetY);
	}

	if (Mode == MODE_AUTOMOVING)
	{
		short x = DrawDragContent_Lerp(AutoMoveStartX, AutoMoveCard->LastX);
		short y = DrawDragContent_Lerp(AutoMoveStartY, AutoMoveCard->LastY);
		DrawCardFront(AutoMoveCard->Suit, AutoMoveCard->Value, x, y);
	}
}

Screen *InitGameScreen(void)
{
	This.OnActivate = OnActivate;
	This.OnUpdate = OnUpdate;
	This.OnKeyDown = OnKeyDown;
	This.OnMouseDown = OnMouseDown;
	This.OnMouseUp = OnMouseUp;
	This.DrawDragContent = DrawDragContent;
	return &This;
}
